package Algorithm.math;

import Common.Utils.StringTools;
import Common.Utils.UTFactory;
import Top100.BitOperation;
import org.junit.Test;

/**
 * @author 蔚蔚樱
 * @version 1.0
 * @date 2021/1/6
 * @author—Email micromicrohard@outlook.com
 * @blogURL https://blog.csdn.net/Micro_Micro_Hard
 * @description 判断一个数是2的N次方(N是整数)
 * 整数（integer）是正整数、零、负整数的集合
 * input: 字符串类型(包括：正负整数、正负小数、零)
 * output: true or false
 * <p>
 * 解题思路：
 * 1、判断输入的字符串是否仅含有正整数或者0开头的小数，负数不可能是 2的N次方
 * 2、判断输入的字符串是否是正整数和0：是的话，在整数范围内进行2的N次方的判定
 * 3、转成double小数：在小数范围内进行2的N次方的判定
 * <p>
 * Tips:float&double的二进制表示方式
 * long bits = Double.doubleToRawLongBits(value);
 * System.out.println(Long.toBinaryString(bits));
 * 参考blog：
 * float&double二进制表示方式：https://www.jianshu.com/p/dc1086067a94
 * Java输出Double、Float的二进制表示 : https://blog.csdn.net/cainiao1412/article/details/118561796
 * Convert float and double to bit and hexadecimal representation in Java :https://stackoverflow.com/questions/10643754/convert-float-and-double-to-bit-and-hexadecimal-representation-in-java
 */

public class NthPowerOfTwo implements BitOperation {

    @Test // 验证功能：从数据库获取测试数据，用于单元测试
    public void TestFunc() throws Exception {
        UTFactory.FullTest(this.getClass());
    }

    @Test // 调试功能 : 用于复现错误的测试案例
    public void DoubleTrack() throws Exception {
        String input = "0.125";
        String output = "TrueExpr";
        UTFactory.DebugTest(this.getClass(), input, output);
    }

    public boolean checkMethod(String value) {
        if (value == null || value.length() == 0) {
            return false;
        }
        value = value.trim();
        if ("0".equals(value)) {
            return false;
        }
        String pattern = "[\\d]+|^0\\.[\\d]+"; // 正则 grammar 仅包括:正整数或者0开头的小数，负数不可能是 2的N次方
        if (!value.matches(pattern)) {
            return false;
        }
        if (isInteger(value)) { // 如果是数字
            return intCheck(Integer.parseInt(value));
        }
        // 如果是小数
        return doubleCheck(Double.parseDouble(value));
    }

    // 判断一个字符串是否为整数
    public boolean isInteger(String value) {
        StringTools tools = new StringTools();
        return tools.CheckIntegerStr(value);
    }

    /**
     * 因为2的N次方的2进制中只有一个 1
     * 所以可以通过判断 2进制中1的个数来判断
     * 或者：2的N次方 & (2的N次方 - 1) 一定是 0
     */
    public boolean intCheck(int value) {
        /*BitCount tools = new BitCount();       // 方法一：2的N次方 的2进制中只有一个 1
        return tools.bitCountComplementMethod1(value) == 1;*/

        return (value & (value - 1)) == 0;       // 方法二：0&上任何数都是0,但是0不是2的N次方，所以需要提前排除0
    }

    public boolean doubleCheck(double value) {
        long bits = Double.doubleToRawLongBits(value);
        //System.out.println(Long.toBinaryString(bits));
        // 位运算:向左移动k 或者 向右移动bits 都可以

        // 向左移动k, attention: k不可以定义成int,不然位数不够
        /*long k = 1;
        for (int i = 0; i < 52; i++) {
            if ((k & bits) != 0) {
                //System.out.println("非2的N次方" + k);
                return false;
            }
            k <<= 1;
        }*/

        // 向右移动bits, attention: 无符号向右移动是 >>>，为什么本题使用>>>和>>都可以？因为只移动52位
        for (int i = 0; i < 52; i++) {
            if ((bits & 1) != 0) {
                return false;
            }
            bits >>>= 1;  //bits >>= 1;
        }
        return true;
    }

    /**
     * 进制的表示方式
     * int x = 0b10;// 二进制 grammar   以0b开头(零和b)的数表示二进制
     * int a = 012; // 八进制 grammar   以"0"(零)开头的数表示八进制
     * int y = 10;  // 十进制 grammar   平时表示的方法便是十进制
     * int z = 0xF; // 十六进制 grammar 以0x开头的数表示十六进制
     */

    public static void main(String[] args) {

        long k = 1;
        long bitss = Double.doubleToLongBits(0.215);
        System.out.println("\n\n\n=========");
        System.out.println(Long.toBinaryString(bitss));
        for (int i = 0; i < 52; i++) {
            if ((k & bitss) != 0) {
                System.out.println("非2的N次方");
                break;
            }
            k <<= 1;
        }
    }
}
